#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctime>

#include <calcLib.h>
#include "protocol.h"

using namespace std;

#define MAXSIZE 200                     //定义buffer数组长度

int protoresentcount = 0;               //重发所支持的传输协议的次数，最大三次
int ansresentcount = 0;                 //重发计算结果的次数，最大三次

bool ifprotosent = false;               //标记服务端是否已经返回了对协议的回应，如果没有则继续重发三次
bool ifanswersent = false;              //标记服务端是否已经返回了对答案的回应，如果没有则继续重发三次

int socket_fd;                          //客户端套接字
struct sockaddr_in server_addr;         //服务端地址
int server_len;                         //服务端地址结构体的长度
int send_num;                           //统计接受服务端的字节数
int recv_num;                           //统计发送给服务端的字节数
char send_buf[MAXSIZE];                 //存储发送信息的数组
char recv_buf[MAXSIZE];                 //存储接收到的信息的数组
int id;                                 //客户端的唯一标识符
struct calcProtocol result;             //题目的答案准备发送给服务端
struct calcMessage msg;                 //客户端支持的协议信息

struct itimerval prototimer;            //用于重发协议请求的timer
struct itimerval anstimer;           //用于重发答案的timer

bool ifprotolost = false;
bool ifanswerlost = false;
bool ifanotherid = false;

void setmissmode(int mode) {
    if (mode == 0) {
        ifprotolost = true;
    } else if (mode == 1) {
        ifanswerlost = true;
    } else if (mode == 2) {
        ifanotherid = true;
    }
}

void cancel_alarm(struct itimerval &tmp) {
    tmp.it_value.tv_sec = 0;
    tmp.it_value.tv_usec = 0;
    signal(SIGALRM, SIG_IGN);
}

void send_answer(int sig) {
    if (ifanswersent) {    //如果客户端已经收到来自服务端的答案响应的话，则终止重发
        cancel_alarm(anstimer);
        return;
    }
    if (ansresentcount > 3) {    //如果重发答案超过三次，则结束
        printf("[Client] Info: has resent answer for 3 times without any response so terminate the program.\n");
        exit(1);
    } else if (ansresentcount > 0) {
        printf("[Client] Info: message lost, resent the answer for %d time\n", ansresentcount);
    }

    ansresentcount++;    //更新重发次数

    if (ifanswerlost) {    //设置了故意不传输模式
        return;
    }
    //发送计算题答案给服务端
    send_num = sendto(socket_fd, &result, sizeof(result), 0, (struct sockaddr *) &server_addr, server_len);
    if (send_num < 0) {
        perror("[Client] Error: function sendto error");
        printf("[Client] Error: client failed in sending messages to server\n");
        exit(1);
    }
}

void send_protocol(int sig) {
    if (ifprotosent) {    //如果客户端已经收到来自服务端的协议响应的话，则终止重发
        cancel_alarm(prototimer);
        return;
    }
    if (protoresentcount > 3) {    //如果重发协议超过三次，则结束
        printf("[Client] Info: has resent protocol requirement for 3 times without any response so terminate the program.\n");
        exit(1);
    } else if (protoresentcount > 0) {
        printf("[Client] Info: message lost, resent the protocol for %d time\n", protoresentcount);
    } else {
        printf("[Client] Info: sending message to server asking for protocol support\n");
    }

    protoresentcount++;    //更新重发次数

    if (ifprotolost) {    //设置了故意不传输模式
        return;
    }

    //发送第一次消息给服务端
    send_num = sendto(socket_fd, &msg, sizeof(msg), 0, (struct sockaddr *) &server_addr, server_len);
    if (send_num < 0) {
        perror("[Client] Error: function sendto error");
        printf("[Client] Error: client failed in sending messages to server\n");
        exit(1);
    }
}

void init_alarm(__sighandler_t method, struct itimerval &tmpAlarm) {
    tmpAlarm.it_interval.tv_sec = 2;
    tmpAlarm.it_interval.tv_usec = 0;
    tmpAlarm.it_value.tv_sec = 2;
    tmpAlarm.it_value.tv_usec = 0;
    signal(SIGALRM, SIG_IGN);
    signal(SIGALRM, method);
    setitimer(ITIMER_REAL, &tmpAlarm, NULL);
}

int main(int argc, char *argv[]) {
    //判断输入参数
    if (argc > 3) {
        printf("[Client] Error: argument number wrong. dir: %s argument num: (%d)\n", argv[0], argc);
        exit(1);
    } else if (argc == 3) {
        printf("[Client] Info: open deliberate miss sending mode\n");
        if (strcmp(argv[2], "0") == 0) {
            setmissmode(0);
        } else if (strcmp(argv[2], "1") == 0) {
            setmissmode(1);
        } else if (strcmp(argv[2], "2") == 0) {
            setmissmode(2);
        }
    }

    const char *delim = ":";
    const char *server_ip = strtok(argv[1], delim);
    int server_port = atoi(strtok(NULL, delim));

    memset(&server_addr, 0, sizeof(server_addr));    //初始化服务地址信息，以免受到之前存过的东西影响
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(server_ip);
    server_addr.sin_port = htons(server_port);
    server_len = sizeof(server_addr);

    //创建客户端UDP套接字
    printf("[Client] Info: creating socket");
    if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("[Client] Error: creating socket faild");
        exit(1);
    }

    msg.type = 22;
    msg.message = 0;
    msg.protocol = 17;
    msg.major_version = 1;
    msg.minor_version = 0;

    //发送协议协商数据报
    send_protocol(0);
    init_alarm(send_protocol, prototimer);

    recv_num = recvfrom(socket_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &server_addr,
                        (socklen_t * ) & server_len);
    if (recv_num < 0) {
        perror("[Client] Error: function recvfrom error");
        printf("[Client] Error: client failed in receiving messages from server\n");
        exit(1);
    }
    ifprotosent = true;

    struct calcProtocol *protocolResponse = (struct calcProtocol *) recv_buf;
    id = (*protocolResponse).id;
    if (protocolResponse->type == 2) {
        printf("[Client] Error: server do not support the protocol proposed by client\n");
        exit(1);
    } else {
        printf("[Client] Info: server support the protocol proposed by client\n");
    }

    uint32_t arith = protocolResponse->arith;
    result.type = 2;
    result.major_version = 1;
    result.minor_version = 0;
    if (ifanotherid) {
        result.id = 0;
    } else {
        result.id = id;
    }
    result.arith = arith;

    //计算结果
    const char *operation;
    if (arith <= 4) {
        int value1 = protocolResponse->inValue1;
        int value2 = protocolResponse->inValue2;
        result.inValue1 = value1, result.inValue2 = value2;
        if (arith == 1) {
            result.inResult = value1 + value2;
            operation = "add";
        } else if (arith == 2) {
            result.inResult = value1 - value2;
            operation = "sub";
        } else if (arith == 3) {
            result.inResult = value1 * value2;
            operation = "mul";
        } else {
            result.inResult = value1 / value2;
            operation = "div";
        }
        printf("[Client] Info: server -> please answer question \"%s %d %d\"\n", operation, value1, value2);
        printf("[Client] Info: client -> sending answer \"%d\" to server\n", result.inResult);
    } else {
        double fvalue1 = protocolResponse->flValue1;
        double fvalue2 = protocolResponse->flValue2;
        result.flValue1 = fvalue1, result.flValue2 = fvalue2;
        if (arith == 5) {
            result.flResult = fvalue1 + fvalue2;
            operation = "fadd";
        } else if (arith == 6) {
            result.flResult = fvalue1 - fvalue2;
            operation = "fsub";
        } else if (arith == 7) {
            result.flResult = fvalue1 * fvalue2;
            operation = "fmul";
        } else {
            result.flResult = fvalue1 / fvalue2;
            operation = "fdiv";
        }
        printf("[Client] Info: server -> please answer question \"%s %lf %lf\"\n", operation, fvalue1, fvalue2);
        printf("[Client] Info: client -> sending answer \"%lf\" to server\n", result.flResult);
    }

    //发送计算结果
    send_answer(0);
    init_alarm(send_answer, anstimer);

    memset(recv_buf, 0, sizeof(recv_buf));
    recv_num = recvfrom(socket_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &server_addr,
                        (socklen_t * ) & server_len);
    if (recv_num < 0) {
        perror("[Client] Error: function recvfrom error");
        printf("[Client] Error: client failed in receiving messages from server\n");
        exit(1);
    }
    ifanswersent = true;

    struct calcMessage *answerResponse = (struct calcMessage *) recv_buf;
    if (answerResponse->message == 1) {
        printf("[Client] Info: server -> Answer is OK\n");
    } else if (answerResponse->message == 2) {
        printf("[Client] Info: server -> Answer is Not OK\n");
    }

    close(socket_fd);
    printf("[Client] Info: program shutdown\n");

    return 0;
}
